package com.zhang.novel.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.zhang.novel.core.enums.ResponseStatus;
import com.zhang.novel.core.exception.BusinessException;
import com.zhang.novel.core.utils.StringUtil;
import com.zhang.novel.entity.Book;
import com.zhang.novel.search.BookSP;
import com.zhang.novel.service.SearchService;
import com.zhang.novel.vo.EsBookVO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class SearchServiceImpl implements SearchService {

    private final String INDEX = "novel";

    private final String TYPE = "book";

    //    private final JestClient jestClient;
    private final RestHighLevelClient restHighLevelClient;

    @Override
    @SneakyThrows
    @Deprecated
    public void importToEs(Book book) {
        //导入到ES,切换为官方推荐的restHighLevelClient
        EsBookVO esBookVO = new EsBookVO();
        BeanUtils.copyProperties(book, esBookVO, "lastIndexUpdateTime");
        esBookVO.setLastIndexUpdateTime(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(book.getLastIndexUpdateTime()));

        // 创建索引与文档
        IndexRequest indexRequest = new IndexRequest(INDEX).id(book.getId().toString()).source(JSON.toJSONString(esBookVO), XContentType.JSON);

        //执行
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);

        //记录日志
        log.info("添加状态：" + indexResponse.status());
        log.info("添加响应：" + indexResponse.toString());

//        Index action = new Index.Builder(esBookVO).index(INDEX).type(TYPE).id(book.getId().toString()).build();
//
//        jestClient.execute(action);
    }

    @Override
    @SneakyThrows
    public void importToEsBulk(List<Book> books) {
        BulkRequest bulkRequest = new BulkRequest();
        for (Book book : books) {
            EsBookVO esBookVO = new EsBookVO();
            BeanUtils.copyProperties(book, esBookVO, "lastIndexUpdateTime");
            esBookVO.setLastIndexUpdateTime(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(book.getLastIndexUpdateTime()));
            bulkRequest.add(
                    new IndexRequest(INDEX).id(book.getId().toString()).source(JSON.toJSONString(esBookVO), XContentType.JSON)
            );
        }

        // 执行批处理
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

        log.info("批处理状态：" + bulkResponse.status());
    }

    @SneakyThrows
    @Override
    public PageInfo searchBook(BookSP params, int page, int pageSize) {
        //1. 封装查询参数
        List<EsBookVO> bookList = new ArrayList<>(0);
        //使用搜索引擎搜索
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 构造查询哪个字段
        if (StringUtils.isNoneBlank(params.getKeyword())) {
            boolQueryBuilder = boolQueryBuilder.must(QueryBuilders.queryStringQuery(params.getKeyword()));
        }

        // 作品方向
        if (params.getWorkDirection() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("workDirection", params.getWorkDirection()));
        }

        // 分类
        if (params.getCatId() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catId", params.getCatId()));
        }
        if (params.getBookStatus() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("bookStatus", params.getBookStatus()));
        }

        if (params.getWordCountMin() == null) {
            params.setWordCountMin(0);
        }
        if (params.getWordCountMax() == null) {
            params.setWordCountMax(Integer.MAX_VALUE);
        }

        boolQueryBuilder.filter(QueryBuilders.rangeQuery("wordCount").gte(params.getWordCountMin()).lte(params.getWordCountMax()));

        if (params.getUpdateTimeMin() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("lastIndexUpdateTime").gte(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(params.getUpdateTimeMin())));
        }
        //2. 创建搜索源
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 设置搜索参数
        sourceBuilder.query(boolQueryBuilder);
        //设置排序
        if (params.getSort() != null) {
            sourceBuilder.sort(StringUtil.camelName(params.getSort()), SortOrder.DESC);
        }

        // 设置分页
        sourceBuilder.from((page - 1) * pageSize);
        sourceBuilder.size(pageSize);

        // 高亮字段
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("authorName");
        highlightBuilder.field("bookName");
        highlightBuilder.field("bookDesc");
        highlightBuilder.field("lastIndexName");
        highlightBuilder.field("catName");
        highlightBuilder.preTags("<span style='color:red'>").postTags("</span>");
        highlightBuilder.fragmentSize(20000);
        sourceBuilder.highlighter(highlightBuilder);

        // 执行查询
        SearchRequest searchRequest = new SearchRequest(INDEX);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        TotalHits totalHits = searchResponse.getHits().getTotalHits();
        if (null != totalHits || totalHits.value >= 0) {
            for (SearchHit searchHit : searchResponse.getHits().getHits()) {
                Map<String, Object> source = searchHit.getSourceAsMap();
                //处理高亮片段
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField authorName = highlightFields.get("authorName");
                HighlightField bookName = highlightFields.get("bookName");
                HighlightField bookDesc = highlightFields.get("bookDesc");
                HighlightField lastIndexName = highlightFields.get("lastIndexName");
                HighlightField catName = highlightFields.get("catName");
                if (authorName != null) {
                    Text[] fragments = authorName.fragments();
                    StringBuilder nameTmp = new StringBuilder();
                    for (Text text : fragments) {
                        nameTmp.append(text);
                    }
                    //将高亮片段组装到结果中去
                    source.put("authorName", nameTmp.toString());
                    log.info(source.toString());
                }
                if (bookName != null) {
                    Text[] fragments = bookName.fragments();
                    StringBuilder nameTmp = new StringBuilder();
                    for (Text text : fragments) {
                        nameTmp.append(text);
                    }
                    //将高亮片段组装到结果中去
                    source.put("bookName", nameTmp.toString());
                    log.info(source.toString());
                }
                if (bookDesc != null) {
                    Text[] fragments = bookDesc.fragments();
                    StringBuilder nameTmp = new StringBuilder();
                    for (Text text : fragments) {
                        nameTmp.append(text);
                    }
                    //将高亮片段组装到结果中去
                    source.put("bookDesc", nameTmp.toString());
                    log.info(source.toString());
                }
                if (lastIndexName != null) {
                    Text[] fragments = lastIndexName.fragments();
                    StringBuilder nameTmp = new StringBuilder();
                    for (Text text : fragments) {
                        nameTmp.append(text);
                    }
                    //将高亮片段组装到结果中去
                    source.put("lastIndexName", nameTmp.toString());
                    log.info(source.toString());
                }
                if (catName != null) {
                    Text[] fragments = catName.fragments();
                    StringBuilder nameTmp = new StringBuilder();
                    for (Text text : fragments) {
                        nameTmp.append(text);
                    }
                    //将高亮片段组装到结果中去
                    source.put("catName", nameTmp.toString());
                    log.info(source.toString());
                }
            }

            SearchHits hits = searchResponse.getHits();
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit hit : searchHits) {
                JSONObject jsonObject = new JSONObject(hit.getSourceAsMap());
                log.info("search:{}", jsonObject.toJSONString());
                bookList.add(JSONObject.toJavaObject(jsonObject, EsBookVO.class));
            }
            PageInfo<EsBookVO> pageInfo = new PageInfo<>(bookList);
            pageInfo.setTotal(totalHits.value);
            pageInfo.setPageNum(page);
            pageInfo.setPageSize(pageSize);
            return pageInfo;
        }
        throw new BusinessException(ResponseStatus.ES_SEARCH_FAIL);
    }

//    @SneakyThrows
//    @Override
//    public PageInfo searchBook(BookSP params, int page, int pageSize) {
//        List<EsBookVO> bookList = new ArrayList<>(0);
//
//        //使用搜索引擎搜索
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        // 构造查询哪个字段
//        if (StringUtils.isNoneBlank(params.getKeyword())) {
//            boolQueryBuilder = boolQueryBuilder.must(QueryBuilders.queryStringQuery(params.getKeyword()));
//        }
//
//        // 作品方向
//        if (params.getWorkDirection() != null) {
//            boolQueryBuilder.filter(QueryBuilders.termQuery("workDirection", params.getWorkDirection()));
//        }
//
//        // 分类
//        if (params.getCatId() != null) {
//            boolQueryBuilder.filter(QueryBuilders.termQuery("catId", params.getCatId()));
//        }
//        if (params.getBookStatus() != null) {
//            boolQueryBuilder.filter(QueryBuilders.termQuery("bookStatus", params.getBookStatus()));
//        }
//
//        if (params.getWordCountMin() == null) {
//            params.setWordCountMin(0);
//        }
//        if (params.getWordCountMax() == null) {
//            params.setWordCountMax(Integer.MAX_VALUE);
//        }
//
//        boolQueryBuilder.filter(QueryBuilders.rangeQuery("wordCount").gte(params.getWordCountMin()).lte(params.getWordCountMax()));
//
//        if (params.getUpdateTimeMin() != null) {
//            boolQueryBuilder.filter(QueryBuilders.rangeQuery("lastIndexUpdateTime").gte(new SimpleDateFormat("yyyy/MM/dd HH:mm").format(params.getUpdateTimeMin())));
//        }
//
//
//
//        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//        searchSourceBuilder.query(boolQueryBuilder);
//
//
//        Count count = new Count.Builder().addIndex(INDEX).addType(TYPE)
//                .query(searchSourceBuilder.toString()).build();
//        CountResult results = jestClient.execute(count);
//        Double total = results.getCount();
//
//
//        // 高亮字段
//        HighlightBuilder highlightBuilder = new HighlightBuilder();
//        highlightBuilder.field("authorName");
//        highlightBuilder.field("bookName");
//        highlightBuilder.field("bookDesc");
//        highlightBuilder.field("lastIndexName");
//        highlightBuilder.field("catName");
//        highlightBuilder.preTags("<span style='color:red'>").postTags("</span>");
//        highlightBuilder.fragmentSize(20000);
//        searchSourceBuilder.highlighter(highlightBuilder);
//
//
//        //设置排序
//        if (params.getSort() != null) {
//            searchSourceBuilder.sort(StringUtil.camelName(params.getSort()), SortOrder.DESC);
//        }
//
//        // 设置分页
//        searchSourceBuilder.from((page - 1) * pageSize);
//        searchSourceBuilder.size(pageSize);
//
//        // 构建Search对象
//        Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(INDEX).addType(TYPE).build();
//        log.debug(search.toString());
//        SearchResult result;
//        result = jestClient.execute(search);
//        log.debug(result.getJsonString());
//        if (result.isSucceeded()) {
//
//            Map resultMap = new ObjectMapper().readValue(result.getJsonString(), Map.class);
//            if (resultMap.get("hits") != null) {
//                Map hitsMap = (Map) resultMap.get("hits");
//                if (hitsMap.size() > 0 && hitsMap.get("hits") != null) {
//                    List hitsList = (List) hitsMap.get("hits");
//                    if (hitsList.size() > 0 && result.getSourceAsString() != null) {
//
//                        JavaType jt = new ObjectMapper().getTypeFactory().constructParametricType(ArrayList.class, EsBookVO.class);
//                        bookList = new ObjectMapper().readValue("[" + result.getSourceAsString() + "]", jt);
//
//                        if (bookList != null) {
//                            for (int i = 0; i < bookList.size(); i++) {
//                                hitsMap = (Map) hitsList.get(i);
//                                Map highlightMap = (Map) hitsMap.get("highlight");
//                                if (highlightMap != null && highlightMap.size() > 0) {
//
//                                    List<String> authorNameList = (List<String>) highlightMap.get("authorName");
//                                    if (authorNameList != null && authorNameList.size() > 0) {
//                                        bookList.get(i).setAuthorName(authorNameList.get(0));
//                                    }
//
//                                    List<String> bookNameList = (List<String>) highlightMap.get("bookName");
//                                    if (bookNameList != null && bookNameList.size() > 0) {
//                                        bookList.get(i).setBookName(bookNameList.get(0));
//                                    }
//
//                                    List<String> bookDescList = (List<String>) highlightMap.get("bookDesc");
//                                    if (bookDescList != null && bookDescList.size() > 0) {
//                                        bookList.get(i).setBookDesc(bookDescList.get(0));
//                                    }
//
//                                    List<String> lastIndexNameList = (List<String>) highlightMap.get("lastIndexName");
//                                    if (lastIndexNameList != null && lastIndexNameList.size() > 0) {
//                                        bookList.get(i).setLastIndexName(lastIndexNameList.get(0));
//                                    }
//
//                                    List<String> catNameList = (List<String>) highlightMap.get("catName");
//                                    if (catNameList != null && catNameList.size() > 0) {
//                                        bookList.get(i).setCatName(catNameList.get(0));
//                                    }
//
//
//                                }
//                            }
//
//
//                        }
//                    }
//                }
//            }
//
//            PageInfo<EsBookVO> pageInfo = new PageInfo<>(bookList);
//            pageInfo.setTotal(total.longValue());
//            pageInfo.setPageNum(page);
//            pageInfo.setPageSize(pageSize);
//            return pageInfo;
//        }
//       throw new BusinessException(ResponseStatus.ES_SEARCH_FAIL);
//    }
}